﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mono.Cecil;
using System.IO;

namespace cilada.engine
{
    public class AssemblyUtilities
    {
        /// <summary>
        /// Resolves the dependencies of a given set of assemblies and checks that all the required assemblies are available.
        /// </summary>
        /// <param name="assemblyPaths">The set of assemblies to resolve dependencies for.</param>
        /// <param name="missingAssemblies">The assemblies that could not be found, but are needed to satisfy references.</param>
        /// <returns>True if all the required assemblies were resolved, false otherwise.</returns>
        public static bool RequiredAssembliesFound(IEnumerable<string> assemblyPaths, IEnumerable<string> dependencyDirs, out IEnumerable<string> missingAssemblies, out IEnumerable<string> assemblyDirectories)
        {
            List<AssemblyNameReference> referencedAssemblies = new List<AssemblyNameReference>();

            /* Method
             * foreach path
             *    verify it exists
             *    add all references in the assembly to the references list, if not already there.
             *    
             * foreach referenced assembly
             *    attempt to resolve reference
             *    if could not resolve, add to failed list
             *    
             * if there are any in failed list
             *    show user which are missing.
             **/

            List<string> dirsContainingAssemblies = new List<string>();
            foreach (string path in assemblyPaths)
            {
                if (!File.Exists(path))
                {
                    //Console.WriteLine("Could not find assembly from path "+path);
                    continue;
                }

                FileInfo f = new FileInfo(path);
                if (!dirsContainingAssemblies.Contains(f.DirectoryName))
                {
                    dirsContainingAssemblies.Add(f.DirectoryName);
                }

                // also need a check that the assembly is actually a .net assembly.

                // get the assembly definition
                AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(path);

                // add the referenced assemblies
                if (assembly.MainModule.HasAssemblyReferences)
                {
                    foreach (AssemblyNameReference anr in assembly.MainModule.AssemblyReferences)
                    {
                        // how is the comparison done - is it correct for this purpose?
                        if (!referencedAssemblies.Contains(anr))
                        {
                            referencedAssemblies.Add(anr);
                            //Console.WriteLine(assembly.Name.Name+" -r-> "+anr.Name);
                        }
                    }
                }
            }

            DefaultAssemblyResolver dar = new DefaultAssemblyResolver();
            // add the directories containing the assemblies to the search path
            dirsContainingAssemblies.AddRange(dependencyDirs);
            foreach (string dir in dirsContainingAssemblies)
            {
                dar.AddSearchDirectory(dir);
            }
            // add the dependency resolution 
            assemblyDirectories = dirsContainingAssemblies;

            List<string> _missingAssemblies = new List<string>();
            // attempt to resolve each reference.
            foreach(AssemblyNameReference anr in referencedAssemblies)
            {
                AssemblyDefinition ad = null;
                try
                {
                    //Console.Write("Trying to resolve " + anr.Name + " (" + anr.Version + ") ");
                    ad = dar.Resolve(anr);
                    //Console.WriteLine("Succeded.");
                }
                catch (FileNotFoundException fnfe)
                {
                    //Console.WriteLine("Failed");
                    if (!_missingAssemblies.Contains(anr.FullName))
                    {
                        _missingAssemblies.Add(anr.FullName);
                    }
                    continue;
                    //Console.WriteLine("Could not resolve assembly " + anr.Name + " (" + anr.Version + ")");
                }
            }

            missingAssemblies = _missingAssemblies;
            return _missingAssemblies.Count == 0;
        }
    }
}
